﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Lucene.Net.Search;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using PaliTextReader2.Utilities;
using Lucene.Net.Documents;
using PaliTextReader2.ContentsDSTableAdapters;
using Lucene.Net.Search.Regex;
using Lucene.Net.Highlight;
using Lucene.Net.Analysis.Standard;
using System.IO;
using System.Text.RegularExpressions;

namespace PaliTextReader2
{
    public partial class SearchControl : UserControl
    {
        static string[] StopWords = new string[] { "ca", "va", "vā", "pi", "kho" };
        BooleanQuery m_objAggQry;
        int m_nResultCount;
        int m_nCurrentResult;

        public SearchControl()
        {
            InitializeComponent();
        }

        private void ctlFind_Click(object sender, EventArgs e)
        {
            int CollectionID = ((ContentsDS.CollectionsRow)ctlCollection.SelectedItem).CollectionID;

            if (CollectionID == -2)
                SearchPED();
            else
                SearchTipitaka();
        }

        private void SearchTipitaka()
        {
            if (!FileIndexer.CanonIndexExists)
            {
                if (MessageBox.Show("Search index not found. Create now? This may take several minutes.", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    DriveInfo di = new DriveInfo(Path.GetPathRoot(Utility.CanonIndexDirectory));

                    if (di.AvailableFreeSpace < 200000000)
                        MessageBox.Show("Not enough disk space to create the index.");
                    else
                    {
                        if (FolderPermission.IsFolderWriteable(Utility.CanonIndexDirectory))
                        {
                            IndexingProgressDlg dlg = new IndexingProgressDlg(true);
                            dlg.ShowDialog();
                        }
                        else
                        {
                            MessageBox.Show("You don't have write permissions to\r\n." + Utility.CanonIndexDirectory);
                            return;
                        }
                    }
                }
            }

            if (FileIndexer.CanonIndexExists)
            {
                Cursor.Current = Cursors.WaitCursor;
                ctlFoundBooks.DataSource = null;
                try
                {
                    NodesTableAdapter objNodesTA = new NodesTableAdapter();

                    IndexReader objReader = IndexReader.Open(Utility.CanonIndexDirectory);
                    IndexSearcher objSearcher = new IndexSearcher(objReader);


                    BooleanQuery query2 = new BooleanQuery();

                    if (((ContentsDS.CollectionsRow)ctlCollection.SelectedItem).CollectionID != -1)
                    {
                        ContentsDS.FilesDataTable objFiles = GetSearchFiles();
                        foreach (ContentsDS.FilesRow objFile in objFiles.Rows)
                        {
                            Query objTermQuery = new TermQuery(new Term("file", objFile.FileID.ToString()));
                            query2.Add(objTermQuery, BooleanClause.Occur.SHOULD);
                        }
                    }

                    Query query1 = null;

                    m_objAggQry = new BooleanQuery();

                    if (ctlIgnoreDiac.Checked)
                        query1 = new RegexQuery(new Term("text", Utility.ConvertToRegex(ctlSearchBox.Text.Trim())));
                    else
                    {
                        QueryParser qp = new QueryParser("text", new StandardAnalyzer(StopWords));
                        qp.SetAllowLeadingWildcard(true);
                        qp.SetDefaultOperator(QueryParser.Operator.AND);
                        query1 = qp.Parse(ctlSearchBox.Text.Trim());
                    }

                    if (((ContentsDS.CollectionsRow)ctlCollection.SelectedItem).CollectionID != -1)
                        m_objAggQry.Add(query2, BooleanClause.Occur.MUST);

                    m_objAggQry.Add(query1, BooleanClause.Occur.MUST);

                    Query objRewritten = m_objAggQry.Rewrite(objReader);

                    Hits hits = objSearcher.Search(objRewritten);

                    ContentsDS.NodesDataTable objResults = new ContentsDS.NodesDataTable();

                    for (int i = 0; i < hits.Length(); i++)
                    {
                        Document document = hits.Doc(i);
                        int FileID = Int32.Parse(document.GetField("file").StringValue());
                        ContentsDS.NodesDataTable objNodes = objNodesTA.GetFileName(FileID);
                        //objNodes[0].NodeTitle += " - " + hits.;
                        objResults.ImportRow(objNodes[0]);
                    }

                    ctlFoundBooks.DisplayMember = "NodeTitle";
                    ctlFoundBooks.ValueMember = "NodeID";
                    ctlFoundBooks.DataSource = objResults;

                    if (ctlFoundBooks.Items.Count == 0)
                        ctlResultBrowser.DocumentText = "";

                    ctlSearchBox.Items.Insert(0, ctlSearchBox.Text);

                    if (hits.Length() == 0)
                        ctlLocation.Text = "no results";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }
                finally
                {
                    Cursor.Current = Cursors.Default;
                }
            }
        }

        private void SearchPED()
        {
            if (!FileIndexer.PEDIndexExists)
            {
                if (MessageBox.Show("This will index the dictionary. Click OK to continue.",
                    "Confirm", MessageBoxButtons.OKCancel) == DialogResult.OK)
                {
                    DriveInfo di = new DriveInfo(Path.GetPathRoot(Utility.PEDIndexDirectory));

                    if (di.AvailableFreeSpace < 10000000)
                        MessageBox.Show("Not enough disk space to create the index.");
                    else
                    {
                        if (FolderPermission.IsFolderWriteable(Utility.PEDIndexDirectory))
                        {
                            IndexingProgressDlg dlg = new IndexingProgressDlg(false);
                            dlg.ShowDialog();
                        }
                        else
                        {
                            MessageBox.Show("You don't have write permissions to\r\n." + Utility.PEDIndexDirectory);
                            return;
                        }
                    }
                }
            }

            if (FileIndexer.PEDIndexExists)
            {
                Cursor.Current = Cursors.WaitCursor;
                ctlFoundBooks.DataSource = null;
                try
                {
                    IndexReader objReader = IndexReader.Open(Utility.PEDIndexDirectory);
                    IndexSearcher objSearcher = new IndexSearcher(objReader);

                    Query query1 = null;

                    if (ctlIgnoreDiac.Checked)
                        query1 = new RegexQuery(new Term("text", Utility.ConvertToRegex(ctlSearchBox.Text.Trim())));
                    else
                    {
                        QueryParser qp = new QueryParser("text", new StandardAnalyzer(StopWords));
                        qp.SetAllowLeadingWildcard(true);
                        qp.SetDefaultOperator(QueryParser.Operator.AND);
                        query1 = qp.Parse(ctlSearchBox.Text.Trim());
                    }

                    Query objRewritten = query1.Rewrite(objReader);

                    ScoreDoc[] hits = objSearcher.Search(objRewritten, null, objReader.NumDocs()).scoreDocs;

                    List<DictionaryMatch> arrMatches = new List<DictionaryMatch>(hits.Length);

                    for (int i = 0; i < hits.Length; i++)
                    {
                        Document document = objSearcher.Doc(hits[i].doc);
                        DictionaryMatch objMatch = new DictionaryMatch();
                        objMatch.ID = document.GetField("ID").StringValue();
                        objMatch.Letter = document.GetField("letter").StringValue();
                        arrMatches.Add(objMatch);
                    }

                    ctlFoundBooks.DisplayMember = "letter";
                    ctlFoundBooks.ValueMember = "ID";
                    ctlFoundBooks.DataSource = arrMatches;

                    if (ctlFoundBooks.Items.Count == 0)
                        ctlResultBrowser.DocumentText = "";

                    ctlSearchBox.Items.Insert(0, ctlSearchBox.Text);

                    if (hits.Length == 0)
                        ctlLocation.Text = "no results";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }
                finally
                {
                    Cursor.Current = Cursors.Default;
                }
            }
        }

        private void SearchControl_Load(object sender, EventArgs e)
        {
            ContentsDS.CollectionsDataTable objRootItems = m_objCollectionsTA.GetRootItems(false);
            foreach (ContentsDS.CollectionsRow objRow in objRootItems)
            {
                ctlCollection.Items.Add(objRow);
                ContentsDS.CollectionsDataTable objChildItems = m_objCollectionsTA.GetChildItems(objRow.CollectionID,false);
                foreach (ContentsDS.CollectionsRow objChildRow in objChildItems)
                {
                    objChildRow.Title = "  " + objChildRow.Title;
                    ctlCollection.Items.Add(objChildRow);
                }
            }

            ContentsDS.CollectionsRow objPEDRow = objRootItems.NewCollectionsRow();
            objPEDRow.CollectionID = -2;
            objPEDRow.Title = "<Pali-English dictionary>";
            ctlCollection.Items.Insert(0, objPEDRow);

            ContentsDS.CollectionsRow objAllRow=objRootItems.NewCollectionsRow();
            objAllRow.CollectionID=-1;
            objAllRow.Title = "<All books>";
            ctlCollection.Items.Insert(0,objAllRow);

            ctlCollection.SelectedIndex=0;
        }

        private ContentsDS.FilesDataTable GetSearchFiles()
        {
            ContentsDS.FilesDataTable objRetVal = new ContentsDS.FilesDataTable();

            ContentsDS.CollectionsRow objCollection = (ContentsDS.CollectionsRow)ctlCollection.SelectedItem;

            if (objCollection.IsParentIDNull())
            {
                ContentsDS.CollectionsDataTable objChildCollections = m_objCollectionsTA.GetChildItems(objCollection.CollectionID,false);
                foreach (ContentsDS.CollectionsRow objChildCollection in objChildCollections.Rows)
                    objRetVal.Merge(m_objFilesTA.GetByCollection(objChildCollection.CollectionID));
            }
            else
                objRetVal = m_objFilesTA.GetByCollection(objCollection.CollectionID);

            return objRetVal;
        }

        private void ctlFoundBooks_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ctlFoundBooks.SelectedItem != null)
            {
                int CollectionID = ((ContentsDS.CollectionsRow)ctlCollection.SelectedItem).CollectionID;

                ctlLocation.Text = String.Empty;

                if (CollectionID != -2)
                    ctlLocation.Text = Utility.GetNodePath(Convert.ToInt32(ctlFoundBooks.SelectedValue));

                Cursor.Current = Cursors.WaitCursor;

                string strFileName = String.Empty;

                if (CollectionID == -2)
                {
                    strFileName = ctlFoundBooks.SelectedValue.ToString();
                }
                else
                {
                    ContentsDS.FilesDataTable objFiles = m_objFilesTA.GetByNode(Convert.ToInt32(ctlFoundBooks.SelectedValue));
                    if (objFiles.Count > 0)
                        strFileName = objFiles[0].FileName;
                }

                if (strFileName != String.Empty)
                {
                    string strBookContent = String.Empty;
                    if (CollectionID == -2)
                        strBookContent = Utility.GetBookContent(Utility.PEDDirectory, strFileName);
                    else
                        strBookContent = Utility.GetBookContent(Utility.CanonDirectory, strFileName);

                    string strContent = ctlShowEntire.Checked ? strBookContent : FileIndexer.GetPlainText(strBookContent);

                    strContent = Regex.Replace(strContent, "<(/)?b[^>]*>", string.Empty);
                    strContent = Regex.Replace(strContent, "<(/)?span[^>]*>", string.Empty);

                    string strFragments = string.Empty;

                    if (ctlHighlightExactMatchesOnly.Checked && !ctlIgnoreDiac.Checked)
                    {
                        string strPhrase=ctlSearchBox.Text.Trim().Trim('"');
                        strFragments = strContent.Replace(strPhrase,
                            String.Format("<a name=\"srBkm23\" class=\"sresult\">{0}</a>", strPhrase));
                    }
                    else
                    {
                        Query query = null;

                        if (ctlIgnoreDiac.Checked)
                            query = new RegexQuery(new Term("text", Utility.ConvertToRegex(ctlSearchBox.Text.Trim())));
                        else
                        {
                            StandardAnalyzer objSA = new StandardAnalyzer(StopWords);
                            QueryParser qp = new QueryParser("text", objSA);
                            qp.SetAllowLeadingWildcard(true);
                            qp.SetDefaultOperator(QueryParser.Operator.AND);                       
                            query = qp.Parse(ctlSearchBox.Text.Trim());
                        }

                        IndexReader objReader = null;
                        if (CollectionID == -2)
                            objReader = IndexReader.Open(Utility.PEDIndexDirectory);
                        else
                            objReader = IndexReader.Open(Utility.CanonIndexDirectory);

                        Query objRewritten = query.Rewrite(objReader);
                        //Query objRewritten = query;


                        QueryScorer scorer = new QueryScorer(objRewritten);
                        Formatter formatter = new SimpleHTMLFormatter("<font style='background-color:yellow'>", "</font>");

                        if (ctlShowEntire.Checked)
                            formatter = new SimpleHTMLFormatter("<a name=\"srBkm23\" class=\"sresult\">", "</a>");

                        Highlighter highlighter = new Highlighter(formatter, scorer);

                        if (ctlShowEntire.Checked)
                            highlighter.SetTextFragmenter(new NullFragmenter());
                        else
                            highlighter.SetTextFragmenter(new SimpleFragmenter((int)ctlFragmentSize.Value));

                        TokenStream stream = new StandardAnalyzer().TokenStream("text", new StringReader(strContent));
                        highlighter.SetMaxDocBytesToAnalyze(3000000);
                        strFragments = highlighter.GetBestFragments(stream, strContent, 100, "<hr/>");

                        objReader.Close();
                    }

                    m_nResultCount = 0;
                    m_nCurrentResult = 0;
                    if (ctlShowEntire.Checked)
                    {
                        strFragments = Regex.Replace(strFragments, "srBkm23", new MatchEvaluator(ResultEvaluator));
                        ctlLocation.Text += "\r\n\r\nmatches in this file: " + m_nResultCount.ToString();
                    }

                    ctlResultsNavigation.Visible = ctlShowEntire.Checked;

                    ctlResultBrowser.DocumentText = strFragments;

                    
                }
                Cursor.Current = Cursors.Default;
            }
        }

        private void ctlSearchPhrase_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                ctlFind_Click(null, null);
            else
                PaliKeyboard.HandlePaliKeys(ctlSearchBox, e);
        }

        private void ctlFoundBooks_DoubleClick(object sender, EventArgs e)
        {
            if (ctlFoundBooks.SelectedItem != null)
            {
                int CollectionID = ((ContentsDS.CollectionsRow)ctlCollection.SelectedItem).CollectionID;

                if (CollectionID != -2)
                {
                    BookViewerControl objViewForm = new BookViewerControl(Convert.ToInt32(ctlFoundBooks.SelectedValue));
                    ContentsDS.FilesDataTable objFiles = m_objFilesTA.GetByNode(Convert.ToInt32(ctlFoundBooks.SelectedValue));

                    Program.TopWindow.OpenBookTab(objViewForm, objFiles[0].FileID);
                }
            }
        }

        private void ctlResultBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (ctlShowEntire.Checked)
            {
                mshtml.HTMLDocument CurrentDocument = (mshtml.HTMLDocument)ctlResultBrowser.Document.DomDocument;
                mshtml.IHTMLStyleSheet styleSheet = CurrentDocument.createStyleSheet("", 0);
                StreamReader streamReader = new StreamReader(Application.StartupPath + @"\style.css");
                string text = streamReader.ReadToEnd();
                streamReader.Close();
                styleSheet.cssText = text;
            }
        }

        private string ResultEvaluator(Match objMatch)
        {
            m_nResultCount++;
            return "sr" + m_nResultCount.ToString();
        }

        private void ctlFirstResult_Click(object sender, EventArgs e)
        {
            if (ctlResultBrowser.Document.All["sr1"]!=null)
                ctlResultBrowser.Document.All["sr1"].ScrollIntoView(false);

            m_nCurrentResult = 1;
        }

        private void ctlPrevResult_Click(object sender, EventArgs e)
        {
            if(m_nCurrentResult!=1)
                m_nCurrentResult--;

            if (ctlResultBrowser.Document.All["sr" + m_nCurrentResult.ToString()] != null)
                ctlResultBrowser.Document.All["sr" + m_nCurrentResult.ToString()].ScrollIntoView(false);
        }

        private void ctlNextResult_Click(object sender, EventArgs e)
        {
            if (m_nCurrentResult != m_nResultCount)
                m_nCurrentResult++;

            if (ctlResultBrowser.Document.All["sr" + m_nCurrentResult.ToString()] != null)
                ctlResultBrowser.Document.All["sr" + m_nCurrentResult.ToString()].ScrollIntoView(false);
        }

        private void ctlLastResult_Click(object sender, EventArgs e)
        {
            if (ctlResultBrowser.Document.All["sr"+m_nResultCount.ToString()] != null)
                ctlResultBrowser.Document.All["sr" + m_nResultCount.ToString()].ScrollIntoView(false);

            m_nCurrentResult = m_nResultCount;
        }

        public void ShowFindDlg()
        {
            IOleCommandTarget cmdt;
            Object o = new object();
            Object i = new object();
            try
            {
                cmdt = (IOleCommandTarget)this.ctlResultBrowser.Document.DomDocument;
                cmdt.Exec(ref ComUtils.CommandGuid, (uint)MiscCommandTarget.Find,
                    0, ref i, ref i);
            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }
        }

        private void ctlIgnoreDiac_CheckedChanged(object sender, EventArgs e)
        {
            ctlHighlightExactMatchesOnly.Checked = !ctlIgnoreDiac.Checked;
            ctlHighlightExactMatchesOnly.Enabled = !ctlIgnoreDiac.Checked;
        }
    }
}
